<!doctype html>
<html lang="zh"><head>
<title>vue-响应式原理 - 糖羽仙</title>
<meta charset="UTF-8">
<meta name="keywords" content="blog ACG JavaScript VUE IT">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">

<link rel="shortcut icon" href="/images/head/head.jpg" type="image/jpeg" />
<meta name="description" content="vue是基于 Object.defineProperty 来实现数据响应;">
<meta property="og:type" content="article">
<meta property="og:title" content="vue-响应式原理">
<meta property="og:url" content="https://www.tangyuxian.com/2021/02/23/%E5%89%8D%E7%AB%AF/vue/vue-%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86/index.html">
<meta property="og:site_name" content="糖羽仙">
<meta property="og:description" content="vue是基于 Object.defineProperty 来实现数据响应;">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://cdn.tangyuxian.com/PicGoImages/202202271821632.webp">
<meta property="og:image" content="https://user-gold-cdn.xitu.io/2018/2/8/1617554b5e866e59?imageView2/0/w/1280/h/960/format/webp/ignore-error/1">
<meta property="og:image" content="https://cdn.tangyuxian.com/PicGoImages/202202271819467.webp">
<meta property="article:published_time" content="2021-02-23T05:22:13.000Z">
<meta property="article:modified_time" content="2022-11-29T05:34:22.779Z">
<meta property="article:author" content="tangyuxian">
<meta property="article:tag" content="vue">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://cdn.tangyuxian.com/PicGoImages/202202271821632.webp">

<link rel="stylesheet" href="/lib/fancybox/fancybox.css">
<link rel="stylesheet" href="/lib/mdui_043tiny/mdui.css">


<link rel="stylesheet" href="/lib/iconfont/iconfont.css?v=1763992124203">

    <link rel="stylesheet" href="//at.alicdn.com/t/font_2421060_8z08qcz5sq3.css">

<link rel="stylesheet" href="/css/style.css?v=1763992124203">




    
        <link rel="stylesheet" href="/custom.css?v=1763992124203">
    
        <link rel="stylesheet" href="/themes/umaru/index.css?v=1763992124203">
    
        <link rel="stylesheet" href="/themes/asuka/index.css?v=1763992124203">
    



<script src="/lib/mdui_043tiny/mdui.js" async></script>
<script src="/lib/fancybox/fancybox.umd.js" async></script>
<script src="/lib/lax.min.js" async></script>


<script async src="/js/app.js?v=1763992124203"></script>

 

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-1Z7TFL96ZH"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-1Z7TFL96ZH');
</script>


<link rel="stylesheet"  href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/atom-one-dark.min.css">
<!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-shiki-plugin@latest/lib/codeblock.css">
<style>:root{--hl-color:#e1e4e8;--hl-bg:#24292e;--hltools-bg:#1f2428;--hltools-color:#c5c5c5;--hlnumber-bg:#24292e;--hlnumber-color:#444d56;--hlscrollbar-bg:#32383e;--hlexpand-bg:linear-gradient(180deg,rgba(36,41,46,0.1),rgba(36,41,46,0.9))}</style><!-- hexo injector head_end end --><meta name="generator" content="Hexo 7.3.0"></head><body class="nexmoe mdui-drawer-body-left"><div id="nexmoe-background"><div class="nexmoe-bg" style="background-image: url(/images/background/xiaomai.jpg)"></div><div class="nexmoe-character"></div><div class="mdui-appbar mdui-shadow-0"><div class="mdui-toolbar"><a class="mdui-btn mdui-btn-icon mdui-ripple" mdui-drawer="{target: &#039;#drawer&#039;, swipe: true}" title="menu"><i class="mdui-icon nexmoefont icon-menu"></i></a><div class="mdui-toolbar-spacer"></div><a class="mdui-btn mdui-btn-icon" href="/" title="tangyuxian"><img src="/images/head/head.jpg" alt="tangyuxian"></a></div></div></div><div id="nexmoe-header"><div class="nexmoe-drawer mdui-drawer" id="drawer">
    <div class="nexmoe-avatar mdui-ripple">
        <a href="/" title="tangyuxian">
            <img src="/images/head/head.jpg" alt="tangyuxian" alt="tangyuxian">
        </a>
    </div>
    <div class="nexmoe-count">
        <div class="nexmoe-count-item"><span>文章</span>84 <div class="item-radius"></div><div class="item-radius item-right"></div> </div>
        <div class="nexmoe-count-item"><span>标签</span>38<div class="item-radius"></div><div class="item-radius item-right"></div></div>
        <div class="nexmoe-count-item"><span>分类</span>5<div class="item-radius"></div><div class="item-radius item-right"></div></div>
    </div>
    <div class="nexmoe-list mdui-list" mdui-collapse="{accordion: true}">
        
        <a class="nexmoe-list-item mdui-list-item mdui-ripple false" href="/" title="回到首页">
            <i class="mdui-list-item-icon nexmoefont icon-meishi"></i>
            <div class="mdui-list-item-content">
                回到首页
            </div>
        </a>
        
        <a class="nexmoe-list-item mdui-list-item mdui-ripple false" href="/archives.html" title="文章归档">
            <i class="mdui-list-item-icon nexmoefont icon-hanbao1"></i>
            <div class="mdui-list-item-content">
                文章归档
            </div>
        </a>
        
        <a class="nexmoe-list-item mdui-list-item mdui-ripple false" href="/about.html" title="关于糖糖">
            <i class="mdui-list-item-icon nexmoefont icon-jiubei1"></i>
            <div class="mdui-list-item-content">
                关于糖糖
            </div>
        </a>
        
        <a class="nexmoe-list-item mdui-list-item mdui-ripple false" href="/friend.html" title="我的朋友">
            <i class="mdui-list-item-icon nexmoefont icon-cola"></i>
            <div class="mdui-list-item-content">
                我的朋友
            </div>
        </a>
        
    </div>
    
    
        
        <div class="nexmoe-widget-wrap">
    <div class="nexmoe-widget nexmoe-search">
         
            <form id="search_form" action_e="https://cn.bing.com/search?q=site:tangyuxian.com" onsubmit="return search();">
                <label><input id="search_value" name="q" type="search" placeholder="搜索"></label>
            </form>
         
    </div>
</div>




    
        
        <div class="nexmoe-widget-wrap">
	<div class="nexmoe-widget nexmoe-social">
		<a
			class="mdui-ripple"
			href="http://wpa.qq.com/msgrd?v=3&uin=2120252100&site=qq&menu=yes"
			target="_blank"
			mdui-tooltip="{content: 'QQ'}"
			style="
				color: rgb(64, 196, 255);
				background-color: rgba(64, 196, 255, .1);
			"
		>
			<i
				class="nexmoefont icon-QQ"
			></i> </a
		><a
			class="mdui-ripple"
			href="mailto:tangyuxian@vip.qq.com"
			target="_blank"
			mdui-tooltip="{content: 'mail'}"
			style="
				color: rgb(249,8,8);
				background-color: rgba(249,8,8,.1);
			"
		>
			<i
				class="nexmoefont icon-mail-fill"
			></i> </a
		><a
			class="mdui-ripple"
			href="https://blog.csdn.net/qq_40621378?type=blog"
			target="_blank"
			mdui-tooltip="{content: 'CSDN'}"
			style="
				color: rgb(199,29,35);
				background-color: rgba(199,29,35,.1);
			"
		>
			<i
				class="nexmoefont icon-csdn"
			></i> </a
		><a
			class="mdui-ripple"
			href="https://www.cnblogs.com/lovetangyuxian/"
			target="_blank"
			mdui-tooltip="{content: '博客园'}"
			style="
				color: rgb(66, 214, 29);
				background-color: rgba(66, 214, 29, .1);
			"
		>
			<i
				class="nexmoefont icon-bokeyuan"
			></i> </a
		><a
			class="mdui-ripple"
			href="https://github.com/tangyuxian/"
			target="_blank"
			mdui-tooltip="{content: 'GitHub'}"
			style="
				color: rgb(25, 23, 23);
				background-color: rgba(25, 23, 23, .15);
			"
		>
			<i
				class="nexmoefont icon-github"
			></i> </a
		><a
			class="mdui-ripple"
			href="https://gitee.com/tangyuxian"
			target="_blank"
			mdui-tooltip="{content: 'gitee'}"
			style="
				color: rgb(255, 255, 255);
				background-color: rgb(199,29,35);
			"
		>
			<i
				class="nexmoefont icon-mayun"
			></i> </a
		>
	</div>
</div>

    
        
        
  <div class="nexmoe-widget-wrap">
    <h3 class="nexmoe-widget-title">文章分类</h3>
    <div class="nexmoe-widget">

      <ul class="category-list">

        


        

        

        <li class="category-list-item">
          <a class="category-list-link" href="/categories/其它/">其它</a>
          <span class="category-list-count">2</span>
        </li>

        

        <li class="category-list-item">
          <a class="category-list-link" href="/categories/前端/">前端</a>
          <span class="category-list-count">62</span>
        </li>

        

        <li class="category-list-item">
          <a class="category-list-link" href="/categories/后端/">后端</a>
          <span class="category-list-count">8</span>
        </li>

        

        <li class="category-list-item">
          <a class="category-list-link" href="/categories/开发工具/">开发工具</a>
          <span class="category-list-count">11</span>
        </li>

        

        <li class="category-list-item">
          <a class="category-list-link" href="/categories/服务器端/">服务器端</a>
          <span class="category-list-count">1</span>
        </li>

        
      </ul>

    </div>
  </div>


    
        
        
  <div class="nexmoe-widget-wrap">
    <div id="randomtagcloud" class="nexmoe-widget tagcloud nexmoe-rainbow">
      <a href="/tags/JavaScript/" style="font-size: 15.56px;">JavaScript</a> <a href="/tags/Jetbrains/" style="font-size: 10px;">Jetbrains</a> <a href="/tags/TypeScript/" style="font-size: 10px;">TypeScript</a> <a href="/tags/UI%E5%BA%93/" style="font-size: 13.33px;">UI库</a> <a href="/tags/centos/" style="font-size: 10px;">centos</a> <a href="/tags/css/" style="font-size: 11.11px;">css</a> <a href="/tags/docker/" style="font-size: 10px;">docker</a> <a href="/tags/echarts/" style="font-size: 10px;">echarts</a> <a href="/tags/elementPlus/" style="font-size: 13.33px;">elementPlus</a> <a href="/tags/es6/" style="font-size: 14.44px;">es6</a> <a href="/tags/git/" style="font-size: 13.33px;">git</a> <a href="/tags/github/" style="font-size: 10px;">github</a> <a href="/tags/html/" style="font-size: 10px;">html</a> <a href="/tags/http/" style="font-size: 13.33px;">http</a> <a href="/tags/java/" style="font-size: 11.11px;">java</a> <a href="/tags/markerdown/" style="font-size: 11.11px;">markerdown</a> <a href="/tags/nginx/" style="font-size: 10px;">nginx</a> <a href="/tags/node/" style="font-size: 13.33px;">node</a> <a href="/tags/vite/" style="font-size: 10px;">vite</a> <a href="/tags/vue/" style="font-size: 18.89px;">vue</a> <a href="/tags/webpack/" style="font-size: 10px;">webpack</a> <a href="/tags/xuex/" style="font-size: 10px;">xuex</a> <a href="/tags/%E4%BA%8B%E4%BB%B6%E6%B5%81/" style="font-size: 10px;">事件流</a> <a href="/tags/%E4%BA%92%E8%81%94%E7%BD%91/" style="font-size: 10px;">互联网</a> <a href="/tags/%E5%8A%A0%E5%AF%86/" style="font-size: 12.22px;">加密</a> <a href="/tags/%E5%8E%9F%E5%88%9B/" style="font-size: 20px;">原创</a> <a href="/tags/%E5%B7%A5%E5%85%B7/" style="font-size: 13.33px;">工具</a> <a href="/tags/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/" style="font-size: 12.22px;">微信小程序</a> <a href="/tags/%E6%80%BB%E7%BB%93/" style="font-size: 13.33px;">总结</a> <a href="/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" style="font-size: 10px;">数据结构</a> <a href="/tags/%E6%9C%8D%E5%8A%A1%E5%99%A8/" style="font-size: 10px;">服务器</a> <a href="/tags/%E6%B5%8F%E8%A7%88%E5%99%A8/" style="font-size: 17.78px;">浏览器</a> <a href="/tags/%E7%AE%97%E6%B3%95/" style="font-size: 14.44px;">算法</a> <a href="/tags/%E7%BC%96%E8%A7%A3%E7%A0%81/" style="font-size: 10px;">编解码</a> <a href="/tags/%E7%BE%8E%E5%8C%96/" style="font-size: 12.22px;">美化</a> <a href="/tags/%E8%A7%86%E9%A2%91/" style="font-size: 11.11px;">视频</a> <a href="/tags/%E8%AE%B0%E5%BD%95/" style="font-size: 11.11px;">记录</a> <a href="/tags/%E8%BD%AC%E8%BD%BD/" style="font-size: 16.67px;">转载</a>
    </div>
    
      <script>
        var maxTagcloud = parseInt(17);
        var tags_length = parseInt(38);
        var tags_arr = [];
        for(var i = 0; i < tags_length; i++){
          tags_arr.push(i);
        }
        tags_arr.sort(function (l, r) {
          return Math.random() > 0.5 ? -1 : 1;
        });
        tags_arr = tags_arr.slice(0, maxTagcloud < tags_length ? tags_length - maxTagcloud : 0);
        for(var tag_i = 0; tag_i < tags_arr.length; tag_i++){
          document.getElementById("randomtagcloud").children[tags_arr[tag_i]].style.display = 'none';
        }
      </script>
    
  </div>

    
        
        
        
  <div class="nexmoe-widget-wrap">
    <h3 class="nexmoe-widget-title">文章归档</h3>
    <div class="nexmoe-widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2025/">2025</a><span class="archive-list-count">5</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2023/">2023</a><span class="archive-list-count">2</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2022/">2022</a><span class="archive-list-count">23</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/">2021</a><span class="archive-list-count">54</span></li></ul>
    </div>
  </div>



    
        
        
  <div class="nexmoe-widget-wrap">
    <h3 class="nexmoe-widget-title">最新文章</h3>
    <div class="nexmoe-widget">
      <ul>
        
          <li>
            <a href="/2025/11/12/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/%E7%BE%8E%E5%8C%96/%E7%BE%8E%E5%8C%96-%E7%BD%91%E9%A1%B5%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E7%9A%84%E6%B8%90%E5%8F%98%E5%8A%A8%E7%94%BB/">美化-网页主题切换的渐变动画</a>
          </li>
        
          <li>
            <a href="/2025/09/29/%E5%89%8D%E7%AB%AF/css/css-%E8%A1%A8%E6%A0%BCtd%E4%B8%8B%E9%AB%98%E5%BA%A6%E4%B8%A2%E5%A4%B1%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/">css-表格td下高度丢失解决办法</a>
          </li>
        
          <li>
            <a href="/2025/09/23/%E5%89%8D%E7%AB%AF/vue/vue-%E5%B0%81%E8%A3%85%E5%87%BD%E6%95%B0%E5%BC%8F%E8%A7%A6%E5%8F%91%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95/">vue-封装函数式触发右键菜单组件</a>
          </li>
        
          <li>
            <a href="/2025/06/04/%E5%89%8D%E7%AB%AF/UI%E5%BA%93/UI%E5%BA%93-ElTooltip%E5%8F%AA%E5%9C%A8%E6%96%87%E5%AD%97%E8%B6%85%E5%87%BA%E8%8C%83%E5%9B%B4%E6%97%B6%E6%98%BE%E7%A4%BA%E7%9A%84%E8%A7%86%E7%BA%BF%E6%96%B9%E5%BC%8F/">UI库-ElTooltip只在文字超出范围时显示的视线方式</a>
          </li>
        
          <li>
            <a href="/2025/05/29/%E5%89%8D%E7%AB%AF/UI%E5%BA%93/UI%E5%BA%93-%E5%B0%81%E8%A3%85PageTable%E6%80%BB%E8%A7%88/">UI库-封装PageTable总览</a>
          </li>
        
      </ul>
    </div>
  </div>

    
        
        <div class="nexmoe-widget-wrap">
    <div class="nexmoe-widget nexmoe-link">
		<ul>
        
            <li>
                <a href="https://tangyuxian.com/" target="_blank" >
                    <img src="https://cdn.tangyuxian.com/PicGoImages/202304272004917.jpg" alt="糖糖第一可爱"></img>
                    <p style="color: var(--color-primary)">糖糖第一可爱</p>
                </a>
            </li>
        
		</ul>
    </div>
</div>
<style>
.nexmoe-widget-wrap .nexmoe-link ul li a {
    text-align : center;
}
.nexmoe-widget-wrap .nexmoe-link ul li a img {
    max-width : 100%;
}
.nexmoe-widget-wrap .nexmoe-link ul li a p {
    margin: 10px 0;
}
</style>

    
   
    <div class="nexmoe-copyright">
        &copy; 2025 tangyuxian
        Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
        & <a href="https://github.com/tangyuxian/hexo-theme-tangyuxian" target="_blank">Tangyuxian</a>
        <br><a target="_blank" href="https://beian.miit.gov.cn/">辽ICP备2021002341号</a>
<div style="font-size: 12px">
   <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
   本站总访问量  <a id="busuanzi_value_site_pv"></a> 次<br />
   本站访客数<a id="busuanzi_value_site_uv"></a>人次
</div>

    </div>
</div><!-- .nexmoe-drawer --></div><div id="nexmoe-content"><div class="nexmoe-primary"><div class="nexmoe-post">
  <article>
    
        <div class="nexmoe-post-cover">
            <img src="/images/post/vue.jpg" alt="vue-响应式原理" loading="lazy">
            <h1>vue-响应式原理</h1>
        </div>
        
    
    <div class="nexmoe-post-meta">
    <div class="nexmoe-rainbow">
        <a class="nexmoefont icon-calendar-fill">2021年02月23日</a>
        
            <a class="nexmoefont icon-appstore-fill -link" href="/categories/%E5%89%8D%E7%AB%AF/">前端</a>
        
        
    </div>
    
    
    
    
    
</div>

    <p>vue是基于 Object.defineProperty 来实现数据响应;</p>
<span id="more"></span>

<h2 id="总览"><a href="#总览" class="headerlink" title="总览"></a>总览</h2><h4 id="根据数据类型来做不同处理，数组和对象类型当值变化时如何劫持。"><a href="#根据数据类型来做不同处理，数组和对象类型当值变化时如何劫持。" class="headerlink" title="根据数据类型来做不同处理，数组和对象类型当值变化时如何劫持。"></a>根据数据类型来做不同处理，数组和对象类型当值变化时如何劫持。</h4><ol>
<li><p>对象内部通过defineReactive方法，使用 Object.defineProperty() 监听数据属性的 get 来进行数据依赖收集，再通过 set 来完成数据更新的派发；</p>
</li>
<li><p>数组则通过重写数组方法来实现的。扩展它的 7 个变更⽅法，通过监听这些方法可以做到依赖收集和派发更新；</p>
</li>
</ol>
<p>多层对象是通过递归来实现劫持，vue3中是使用 proxy来实现响应式数据</p>
<p>补充回答：</p>
<p>内部依赖收集是怎么做到的，每个属性都拥有自己的dep属性，存放他所依赖的 watcher，当属性变化后会通知自己对应的 watcher去更新。</p>
<h4 id="响应式流程："><a href="#响应式流程：" class="headerlink" title="响应式流程："></a>响应式流程：</h4><p>1､defineReactive  把数据定义成响应式的；</p>
<p>2､给属性增加一个 dep，用来收集对应的那些watcher；</p>
<p>3､等数据变化进行更新</p>
<p>dep.depend() &#x2F;&#x2F; get 取值：进行依赖收集</p>
<p>dep.notify() &#x2F;&#x2F; set 设置时：通知视图更新</p>
<h4 id="性能优化相关的内容："><a href="#性能优化相关的内容：" class="headerlink" title="性能优化相关的内容："></a>性能优化相关的内容：</h4><p>1)对象层级过深，性能就会差。</p>
<p>2)不需要响应数据的内容不要放在data中。</p>
<p>3)object.freeze()  可以冻结数据。</p>
<h2 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h2><h4 id="源码地址"><a href="#源码地址" class="headerlink" title="源码地址"></a>源码地址</h4><p><a target="_blank" rel="noopener" href="https://github.com/vuejs/vue/blob/dev/src/core/observer/index.js">https://github.com/vuejs/vue/blob/dev/src/core/observer/index.js</a></p>
<h4 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h4><h6 id="初步"><a href="#初步" class="headerlink" title="初步"></a>初步</h6><p>最近一段时间在阅读Vue源码，从它的核心原理入手，开始了源码的学习，而其核心原理就是其数据的响应式，讲到Vue的响应式原理，我们可以从它的兼容性说起，Vue不支持IE8以下版本的浏览器，因为Vue是基于 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty">Object.defineProperty</a> 来实现数据响应的，而        Object.defineProperty 是 ES5 中一个无法 shim 的特性，这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因；Vue通过Object.defineProperty的 <strong>getter&#x2F;setter</strong> 对收集的依赖项进行监听,在属性被访问和修改时通知变化,进而更新视图数据；</p>
<p>受现代JavaScript 的限制 (以及废弃 <strong>Object.observe</strong>)，Vue不能检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 <strong>getter&#x2F;setter</strong> 转化过程，所以属性必须在 <strong>data</strong> 对象上存在才能让Vue转换它，这样才能让它是响应的。</p>
<p><img onerror="imgOnError(this);" data-fancybox="gallery" src="https://cdn.tangyuxian.com/PicGoImages/202202271821632.webp" alt="img" data-caption="img" loading="lazy"></p>
<p>我们这里是根据Vue2.3源码进行分析,Vue数据响应式变化主要涉及 <strong>Observer</strong>, <strong>Watcher</strong> , <strong>Dep</strong> 这三个主要的类；因此要弄清Vue响应式变化需要明白这个三个类之间是如何运作联系的；以及它们的原理，负责的逻辑操作。那么我们从一个简单的Vue实例的代码来分析Vue的响应式原理</p>
<figure class="shiki javascript"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></div><div class="code"><pre class="shiki github-dark"><code><span class="line"><span style="color: #F97583">var</span><span style="color: #E1E4E8"> vue </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">new</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Vue</span><span style="color: #E1E4E8">({</span></span>
<span class="line"><span style="color: #E1E4E8">    el: </span><span style="color: #9ECBFF">&quot;#app&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    data: {</span></span>
<span class="line"><span style="color: #E1E4E8">        name: </span><span style="color: #9ECBFF">&#39;Junga&#39;</span></span>
<span class="line"><span style="color: #E1E4E8">    },</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">created</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.</span><span style="color: #B392F0">helloWorld</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    },</span></span>
<span class="line"><span style="color: #E1E4E8">    methods: {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">helloWorld</span><span style="color: #E1E4E8">: </span><span style="color: #F97583">function</span><span style="color: #E1E4E8">() {</span></span>
<span class="line"><span style="color: #E1E4E8">            console.</span><span style="color: #B392F0">log</span><span style="color: #E1E4E8">(</span><span style="color: #9ECBFF">&#39;my name is&#39;</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">+</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.name)</span></span>
<span class="line"><span style="color: #E1E4E8">        }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">...</span></span>
<span class="line"><span style="color: #E1E4E8">})</span></span></code></pre></div></div></figure>



<h5 id="Vue初始化实例"><a href="#Vue初始化实例" class="headerlink" title="Vue初始化实例"></a>Vue初始化实例</h5><p>根据Vue的<a target="_blank" rel="noopener" href="https://cn.vuejs.org/v2/guide/instance.html#%E5%AE%9E%E4%BE%8B%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90">生命周期</a>我们知道，Vue首先会进行init初始化操作；源码在<a target="_blank" rel="noopener" href="https://github.com/tangyuxian/Vue-learn/blob/master/core/instance/init.js">src&#x2F;core&#x2F;instance&#x2F;init.js</a>中</p>
<figure class="shiki javascript"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></div><div class="code"><pre class="shiki github-dark"><code><span class="line"><span style="color: #6A737D">/*初始化生命周期*/</span></span>
<span class="line"><span style="color: #B392F0">initLifecycle</span><span style="color: #E1E4E8">(vm)</span></span>
<span class="line"><span style="color: #6A737D">/*初始化事件*/</span></span>
<span class="line"><span style="color: #B392F0">initEvents</span><span style="color: #E1E4E8">(vm)Object.defineProperty </span></span>
<span class="line"><span style="color: #6A737D">/*初始化render*/</span></span>
<span class="line"><span style="color: #B392F0">initRender</span><span style="color: #E1E4E8">(vm)</span></span>
<span class="line"><span style="color: #6A737D">/*调用beforeCreate钩子函数并且触发beforeCreate钩子事件*/</span></span>
<span class="line"><span style="color: #B392F0">callHook</span><span style="color: #E1E4E8">(vm, </span><span style="color: #9ECBFF">&#39;beforeCreate&#39;</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #B392F0">initInjections</span><span style="color: #E1E4E8">(vm) </span><span style="color: #6A737D">// resolve injections before data/props</span></span>
<span class="line"><span style="color: #6A737D">/*初始化props、methods、data、computed与watch*/</span></span>
<span class="line"><span style="color: #B392F0">initState</span><span style="color: #E1E4E8">(vm)</span></span>
<span class="line"><span style="color: #B392F0">initProvide</span><span style="color: #E1E4E8">(vm) </span><span style="color: #6A737D">// resolve provide after data/props</span></span>
<span class="line"><span style="color: #6A737D">/*调用created钩子函数并且触发created钩子事件*/</span></span>
<span class="line"><span style="color: #B392F0">callHook</span><span style="color: #E1E4E8">(vm, </span><span style="color: #9ECBFF">&#39;created&#39;</span><span style="color: #E1E4E8">)</span></span></code></pre></div></div></figure>

<p>以上代码可以看到 <strong>initState(vm)</strong> 是用来初始化props,methods,data,computed和watch;</p>
<p><a target="_blank" rel="noopener" href="https://github.com/tangyuxian/Vue-learn/blob/master/core/instance/state.js">src&#x2F;core&#x2F;instance&#x2F;state.js</a></p>
<figure class="shiki javascript"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></div><div class="code"><pre class="shiki github-dark"><code><span class="line"><span style="color: #6A737D">/*初始化props、methods、data、computed与watch*/</span></span>
<span class="line"><span style="color: #F97583">export</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">function</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">initState</span><span style="color: #E1E4E8"> (</span><span style="color: #FFAB70">vm</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Component</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">  vm._watchers </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> []</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">const</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">opts</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> vm.$options</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*初始化props*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (opts.props) </span><span style="color: #B392F0">initProps</span><span style="color: #E1E4E8">(vm, opts.props)</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*初始化方法*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (opts.methods) </span><span style="color: #B392F0">initMethods</span><span style="color: #E1E4E8">(vm, opts.methods)</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*初始化data*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (opts.data) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">initData</span><span style="color: #E1E4E8">(vm)</span></span>
<span class="line"><span style="color: #E1E4E8">  } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*该组件没有data的时候绑定一个空对象*/</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">observe</span><span style="color: #E1E4E8">(vm._data </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> {}, </span><span style="color: #79B8FF">true</span><span style="color: #E1E4E8"> </span><span style="color: #6A737D">/* asRootData */</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*初始化computed*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (opts.computed) </span><span style="color: #B392F0">initComputed</span><span style="color: #E1E4E8">(vm, opts.computed)</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*初始化watchers*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (opts.watch) </span><span style="color: #B392F0">initWatch</span><span style="color: #E1E4E8">(vm, opts.watch)</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span>
<span class="line"><span style="color: #F97583">...</span></span>
<span class="line"><span style="color: #6A737D">/*初始化data*/</span></span>
<span class="line"><span style="color: #F97583">function</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">initData</span><span style="color: #E1E4E8"> (</span><span style="color: #FFAB70">vm</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Component</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*得到data数据*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">let</span><span style="color: #E1E4E8"> data </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> vm.$options.data</span></span>
<span class="line"><span style="color: #E1E4E8">  data </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> vm._data </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">typeof</span><span style="color: #E1E4E8"> data </span><span style="color: #F97583">===</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&#39;function&#39;</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">?</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">getData</span><span style="color: #E1E4E8">(data, vm)</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> data </span><span style="color: #F97583">||</span><span style="color: #E1E4E8"> {}defi</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">...</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">//遍历data中的数据</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">while</span><span style="color: #E1E4E8"> (i</span><span style="color: #F97583">--</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*保证data中的key不与props中的key重复，props优先，如果有冲突会产生warning*/</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (props </span><span style="color: #F97583">&amp;&amp;</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">hasOwn</span><span style="color: #E1E4E8">(props, keys[i])) {</span></span>
<span class="line"><span style="color: #E1E4E8">      process.env.</span><span style="color: #79B8FF">NODE_ENV</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">!==</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&#39;production&#39;</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">&amp;&amp;</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">warn</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #9ECBFF">`The data property &quot;${</span><span style="color: #E1E4E8">keys</span><span style="color: #9ECBFF">[</span><span style="color: #E1E4E8">i</span><span style="color: #9ECBFF">]</span><span style="color: #9ECBFF">}&quot; is already declared as a prop. `</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">+</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #9ECBFF">`Use prop default value instead.`</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        vm</span></span>
<span class="line"><span style="color: #E1E4E8">      )</span></span>
<span class="line"><span style="color: #E1E4E8">    } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #F97583">!</span><span style="color: #B392F0">isReserved</span><span style="color: #E1E4E8">(keys[i])) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/*判断是否是保留字段*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/*这里是我们前面讲过的代理，将data上面的属性代理到了vm实例上*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #B392F0">proxy</span><span style="color: #E1E4E8">(vm, </span><span style="color: #9ECBFF">`_data`</span><span style="color: #E1E4E8">, keys[i])</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">// observe data</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*这里通过observe实例化Observe对象，开始对数据进行绑定，asRootData用来根数据，用来计算实例化根数据的个数，下面会进行递归observe进行对深层对象的绑定。则asRootData为非true*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">observe</span><span style="color: #E1E4E8">(data, </span><span style="color: #79B8FF">true</span><span style="color: #E1E4E8"> </span><span style="color: #6A737D">/* asRootData */</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div></div></figure>

<h6 id="1、initData"><a href="#1、initData" class="headerlink" title="1、initData"></a>1、initData</h6><p>现在我们重点分析下<strong>initData</strong>，这里主要做了两件事，一是将_data上面的数据代理到vm上，二是通过执行 observe(data, true &#x2F; <em>asRootData</em> &#x2F;)将所有data变成可观察的，即对data定义的每个属性进行getter&#x2F;setter操作，这里就是Vue实现响应式的基础；<strong>observe</strong>的实现如下 <a target="_blank" rel="noopener" href="https://github.com/tangyuxian/Vue-learn/blob/master/core/observer/index.js">src&#x2F;core&#x2F;observer&#x2F;index.js</a></p>
<figure class="shiki javascript"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></div><div class="code"><pre class="shiki github-dark"><code><span class="line"><span style="color: #E1E4E8"> </span><span style="color: #6A737D">/*尝试创建一个Observer实例（__ob__），如果成功创建Observer实例则返回新的Observer实例，如果已有Observer实例则返回现有的Observer实例。*/</span></span>
<span class="line"><span style="color: #F97583">export</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">function</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">observe</span><span style="color: #E1E4E8"> (</span><span style="color: #FFAB70">value</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">any</span><span style="color: #E1E4E8">, </span><span style="color: #FFAB70">asRootData</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">?</span><span style="color: #79B8FF">boolean</span><span style="color: #E1E4E8">)</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Observer</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">|</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">void</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #F97583">!</span><span style="color: #B392F0">isObject</span><span style="color: #E1E4E8">(value)) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">return</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">let</span><span style="color: #E1E4E8"> ob</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Observer</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">|</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">void</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*这里用__ob__这个属性来判断是否已经有Observer实例，如果没有Observer实例则会新建一个Observer实例并赋值给__ob__这个属性，如果已有Observer实例则直接返回该Observer实例，这里可以看Observer实例化的代码def(value, &#39;__ob__&#39;, this)*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #B392F0">hasOwn</span><span style="color: #E1E4E8">(value, </span><span style="color: #9ECBFF">&#39;__ob__&#39;</span><span style="color: #E1E4E8">) </span><span style="color: #F97583">&amp;&amp;</span><span style="color: #E1E4E8"> value.__ob__ </span><span style="color: #F97583">instanceof</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Observer</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">    ob </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> value.__ob__</span></span>
<span class="line"><span style="color: #E1E4E8">  } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*这里的判断是为了确保value是单纯的对象，而不是函数或者是Regexp等情况。而且该对象在shouldConvert的时候才会进行Observer。这是一个标识位，避免重复对value进行Observer</span></span>
<span class="line"><span style="color: #6A737D">    */</span></span>
<span class="line"><span style="color: #E1E4E8">    observerState.shouldConvert </span><span style="color: #F97583">&amp;&amp;</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">!</span><span style="color: #B392F0">isServerRendering</span><span style="color: #E1E4E8">() </span><span style="color: #F97583">&amp;&amp;</span></span>
<span class="line"><span style="color: #E1E4E8">    (Array.</span><span style="color: #B392F0">isArray</span><span style="color: #E1E4E8">(value) </span><span style="color: #F97583">||</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">isPlainObject</span><span style="color: #E1E4E8">(value)) </span><span style="color: #F97583">&amp;&amp;</span></span>
<span class="line"><span style="color: #E1E4E8">    Object.</span><span style="color: #B392F0">isExtensible</span><span style="color: #E1E4E8">(value) </span><span style="color: #F97583">&amp;&amp;</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">!</span><span style="color: #E1E4E8">value._isVue</span></span>
<span class="line"><span style="color: #E1E4E8">  ) {</span></span>
<span class="line"><span style="color: #E1E4E8">    ob </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">new</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Observer</span><span style="color: #E1E4E8">(value)</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (asRootData </span><span style="color: #F97583">&amp;&amp;</span><span style="color: #E1E4E8"> ob) {</span></span>
<span class="line"><span style="color: #E1E4E8">     </span><span style="color: #6A737D">/*如果是根数据则计数，后面Observer中的observe的asRootData非true*/</span></span>
<span class="line"><span style="color: #E1E4E8">    ob.vmCount</span><span style="color: #F97583">++</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">return</span><span style="color: #E1E4E8"> ob</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div></div></figure>

<p>这里 <strong>new Observer(value)</strong> 就是实现响应式的核心方法之一了，通过它将data转变可以成观察的，而这里正是我们开头说的，用了 <strong>Object.defineProperty</strong> 实现了data的 <strong>getter&#x2F;setter</strong> 操作，通过 <strong>Watcher</strong> 来观察数据的变化，进而更新到视图中。</p>
<h6 id="2、Observer"><a href="#2、Observer" class="headerlink" title="2、Observer"></a>2、Observer</h6><p>Observer类是将每个目标对象（即data）的键值转换成getter&#x2F;setter形式，用于进行依赖收集以及调度更新。</p>
<p><a target="_blank" rel="noopener" href="https://github.com/tangyuxian/Vue-learn/blob/master/core/observer/index.js">src&#x2F;core&#x2F;observer&#x2F;index.js</a></p>
<figure class="shiki javascript"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></div><div class="code"><pre class="shiki github-dark"><code><span class="line"><span style="color: #F97583">export</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Observer</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">value</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">any</span><span style="color: #E1E4E8">;</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">dep</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Dep</span><span style="color: #E1E4E8">;</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">vmCount</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">number</span><span style="color: #E1E4E8">; </span><span style="color: #6A737D">// number of vms that has this object as root $data</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">constructor</span><span style="color: #E1E4E8"> (</span><span style="color: #FFAB70">value</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">any</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.value </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> value</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.dep </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">new</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Dep</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.vmCount </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">0</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/* 将Observer实例绑定到data的__ob__属性上面去，之前说过observe的时候会先检测是否已经有__ob__对象存放Observer实例了，def方法定义可以参考/src/core/util/lang.js*/</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">def</span><span style="color: #E1E4E8">(value, </span><span style="color: #9ECBFF">&#39;__ob__&#39;</span><span style="color: #E1E4E8">, </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (Array.</span><span style="color: #B392F0">isArray</span><span style="color: #E1E4E8">(value)) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/*如果是数组，将修改后可以截获响应的数组方法替换掉该数组的原型中的原生方法，达到监听数组数据变化响应的效果。这里如果当前浏览器支持__proto__属性，则直接覆盖当前数组对象原型上的原生数组方法，如果不支持该属性，则直接覆盖数组对象的原型。*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">const</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">augment</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> hasProto</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #F97583">?</span><span style="color: #E1E4E8"> protoAugment  </span><span style="color: #6A737D">/*直接覆盖原型的方法来修改目标对象*/</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> copyAugment   </span><span style="color: #6A737D">/*定义（覆盖）目标对象或数组的某一个方法*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #B392F0">augment</span><span style="color: #E1E4E8">(value, arrayMethods, arrayKeys)</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/*如果是数组则需要遍历数组的每一个成员进行observe*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.</span><span style="color: #B392F0">observeArray</span><span style="color: #E1E4E8">(value)</span></span>
<span class="line"><span style="color: #E1E4E8">    } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/*如果是对象则直接walk进行绑定*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.</span><span style="color: #B392F0">walk</span><span style="color: #E1E4E8">(value)</span></span>
<span class="line"><span style="color: #E1E4E8">    },</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">walk</span><span style="color: #E1E4E8"> (obj: Object) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">const</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">keys</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> Object.</span><span style="color: #B392F0">keys</span><span style="color: #E1E4E8">(obj)</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/*walk方法会遍历对象的每一个属性进行defineReactive绑定*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">for</span><span style="color: #E1E4E8"> (</span><span style="color: #F97583">let</span><span style="color: #E1E4E8"> i </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">0</span><span style="color: #E1E4E8">; i </span><span style="color: #F97583">&lt;</span><span style="color: #E1E4E8"> keys.</span><span style="color: #79B8FF">length</span><span style="color: #E1E4E8">; i</span><span style="color: #F97583">++</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">defineReactive</span><span style="color: #E1E4E8">(obj, keys[i], obj[keys[i]])</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span></code></pre></div></div></figure>

<ol>
<li>首先将Observer实例绑定到data的<strong>ob</strong>属性上面去，防止重复绑定；</li>
<li>若data为数组，先实现对应的<a target="_blank" rel="noopener" href="https://cn.vuejs.org/v2/guide/list.html#%E5%8F%98%E5%BC%82%E6%96%B9%E6%B3%95">变异方法</a>（这里变异方法是指Vue重写了数组的7种原生方法，这里不做赘述，后续再说明），再将数组的每个成员进行observe，使之成响应式数据；</li>
<li>否则执行walk()方法，遍历data所有的数据，进行getter&#x2F;setter绑定，这里的核心方法就是 <strong>defineReative(obj, keys[i], obj[keys[i]])</strong></li>
</ol>
<figure class="shiki"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></div><div class="code"><pre class="shiki github-dark"><code><span class="line"><span style="color: #e1e4e8">export function defineReactive (</span></span>
<span class="line"><span style="color: #e1e4e8">  obj: Object,</span></span>
<span class="line"><span style="color: #e1e4e8">  key: string,</span></span>
<span class="line"><span style="color: #e1e4e8">  val: any,</span></span>
<span class="line"><span style="color: #e1e4e8">  customSetter?: Function</span></span>
<span class="line"><span style="color: #e1e4e8">) {</span></span>
<span class="line"><span style="color: #e1e4e8">  /*在闭包中定义一个dep对象*/</span></span>
<span class="line"><span style="color: #e1e4e8">  const dep = new Dep()</span></span>
<span class="line"><span style="color: #e1e4e8">  const property = Object.getOwnPropertyDescriptor(obj, key)</span></span>
<span class="line"><span style="color: #e1e4e8">  if (property &amp;&amp; property.configurable === false) {</span></span>
<span class="line"><span style="color: #e1e4e8">    return</span></span>
<span class="line"><span style="color: #e1e4e8">  }</span></span>
<span class="line"><span style="color: #e1e4e8">  /*如果之前该对象已经预设了getter以及setter函数则将其取出来，新定义的getter/setter中会将其执行，保证不会覆盖之前已经定义的getter/setter。*/</span></span>
<span class="line"><span style="color: #e1e4e8">  // cater for pre-defined getter/setters</span></span>
<span class="line"><span style="color: #e1e4e8">  const getter = property &amp;&amp; property.get</span></span>
<span class="line"><span style="color: #e1e4e8">  const setter = property &amp;&amp; property.set</span></span>
<span class="line"><span style="color: #e1e4e8">  /*对象的子对象递归进行observe并返回子节点的Observer对象*/</span></span>
<span class="line"><span style="color: #e1e4e8">  let childOb = observe(val)</span></span>
<span class="line"><span style="color: #e1e4e8">  Object.defineProperty(obj, key, {</span></span>
<span class="line"><span style="color: #e1e4e8">    enumerable: true,</span></span>
<span class="line"><span style="color: #e1e4e8">    configurable: true,</span></span>
<span class="line"><span style="color: #e1e4e8">    get: function reactiveGetter () {</span></span>
<span class="line"><span style="color: #e1e4e8">      /*如果原本对象拥有getter方法则执行*/</span></span>
<span class="line"><span style="color: #e1e4e8">      const value = getter ? getter.call(obj) : val</span></span>
<span class="line"><span style="color: #e1e4e8">      if (Dep.target) {</span></span>
<span class="line"><span style="color: #e1e4e8">        /*进行依赖收集*/</span></span>
<span class="line"><span style="color: #e1e4e8">        dep.depend()</span></span>
<span class="line"><span style="color: #e1e4e8">        if (childOb) {</span></span>
<span class="line"><span style="color: #e1e4e8">          /*子对象进行依赖收集，其实就是将同一个watcher观察者实例放进了两个depend中，一个是正在本身闭包中的depend，另一个是子元素的depend*/</span></span>
<span class="line"><span style="color: #e1e4e8">          childOb.dep.depend()</span></span>
<span class="line"><span style="color: #e1e4e8">        }</span></span>
<span class="line"><span style="color: #e1e4e8">        if (Array.isArray(value)) {</span></span>
<span class="line"><span style="color: #e1e4e8">          /*是数组则需要对每一个成员都进行依赖收集，如果数组的成员还是数组，则递归。*/</span></span>
<span class="line"><span style="color: #e1e4e8">          dependArray(value)</span></span>
<span class="line"><span style="color: #e1e4e8">        }</span></span>
<span class="line"><span style="color: #e1e4e8">      }</span></span>
<span class="line"><span style="color: #e1e4e8">      return value</span></span>
<span class="line"><span style="color: #e1e4e8">    },</span></span>
<span class="line"><span style="color: #e1e4e8">    set: function reactiveSetter (newVal) {</span></span>
<span class="line"><span style="color: #e1e4e8">      /*通过getter方法获取当前值，与新值进行比较，一致则不需要执行下面的操作*/</span></span>
<span class="line"><span style="color: #e1e4e8">      const value = getter ? getter.call(obj) : val</span></span>
<span class="line"><span style="color: #e1e4e8">      /* eslint-disable no-self-compare */</span></span>
<span class="line"><span style="color: #e1e4e8">      if (newVal === value || (newVal !== newVal &amp;&amp; value !== value)) {</span></span>
<span class="line"><span style="color: #e1e4e8">        return</span></span>
<span class="line"><span style="color: #e1e4e8">      }</span></span>
<span class="line"><span style="color: #e1e4e8">      /* eslint-enable no-self-compare */</span></span>
<span class="line"><span style="color: #e1e4e8">      if (process.env.NODE_ENV !== &#39;production&#39; &amp;&amp; customSetter) {</span></span>
<span class="line"><span style="color: #e1e4e8">        customSetter()</span></span>
<span class="line"><span style="color: #e1e4e8">      }</span></span>
<span class="line"><span style="color: #e1e4e8">      if (setter) {</span></span>
<span class="line"><span style="color: #e1e4e8">        /*如果原本对象拥有setter方法则执行setter*/</span></span>
<span class="line"><span style="color: #e1e4e8">        setter.call(obj, newVal)</span></span>
<span class="line"><span style="color: #e1e4e8">      } else {</span></span>
<span class="line"><span style="color: #e1e4e8">        val = newVal</span></span>
<span class="line"><span style="color: #e1e4e8">      }</span></span>
<span class="line"><span style="color: #e1e4e8">      /*新的值需要重新进行observe，保证数据响应式*/</span></span>
<span class="line"><span style="color: #e1e4e8">      childOb = observe(newVal)</span></span>
<span class="line"><span style="color: #e1e4e8">      /*dep对象通知所有的观察者*/</span></span>
<span class="line"><span style="color: #e1e4e8">      dep.notify()</span></span>
<span class="line"><span style="color: #e1e4e8">    }</span></span>
<span class="line"><span style="color: #e1e4e8">  })</span></span>
<span class="line"><span style="color: #e1e4e8">}复制代码</span></span></code></pre></div></div></figure>

<p>其中getter方法：</p>
<ol>
<li>先为每个data声明一个 <strong>Dep</strong> 实例对象，被用于getter时执行dep.depend()进行收集相关的依赖;</li>
<li>根据Dep.target来判断是否收集依赖，还是普通取值。Dep.target是在什么时候，如何收集的后面再说明，先简单了解它的作用，</li>
</ol>
<p>那么问题来了，我们为啥要收集相关依赖呢？</p>
<figure class="shiki javascript"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></div><div class="code"><pre class="shiki github-dark"><code><span class="line"><span style="color: #F97583">new</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Vue</span><span style="color: #E1E4E8">({</span></span>
<span class="line"><span style="color: #E1E4E8">    template: </span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #9ECBFF">`&lt;div&gt;</span></span>
<span class="line"><span style="color: #9ECBFF">            &lt;span&gt;text1:&lt;/span&gt; {{text1}}</span></span>
<span class="line"><span style="color: #9ECBFF">            &lt;span&gt;text2:&lt;/span&gt; {{text2}}</span></span>
<span class="line"><span style="color: #9ECBFF">        &lt;div&gt;`</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    data: {</span></span>
<span class="line"><span style="color: #E1E4E8">        text1: </span><span style="color: #9ECBFF">&#39;text1&#39;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        text2: </span><span style="color: #9ECBFF">&#39;text2&#39;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        text3: </span><span style="color: #9ECBFF">&#39;text3&#39;</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">});</span></span></code></pre></div></div></figure>

<p>我们可以从以上代码看出，data中text3并没有被模板实际用到，为了提高代码执行效率，我们没有必要对其进行响应式处理，因此，依赖收集简单点理解就是收集只在实际页面中用到的data数据，然后打上标记，这里就是标记为Dep.target。</p>
<p>在setter方法中:</p>
<ol>
<li>获取新的值并且进行observe，保证数据响应式；</li>
<li>通过dep对象通知所有观察者去更新数据，从而达到响应式效果。</li>
</ol>
<p>在Observer类中，我们可以看到在getter时，dep会收集相关依赖，即收集依赖的watcher，然后在setter操作时候通过dep去通知watcher,此时watcher就执行变化，我们用一张图描述这三者之间的关系：<br><img onerror="imgOnError(this);" data-fancybox="gallery" src="https://user-gold-cdn.xitu.io/2018/2/8/1617554b5e866e59?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="关系图" data-caption="关系图" loading="lazy"></p>
<p>从图我们可以简单理解：Dep可以看做是书店，Watcher就是书店订阅者，而Observer就是书店的书，订阅者在书店订阅书籍，就可以添加订阅者信息，一旦有新书就会通过书店给订阅者发送消息。</p>
<h6 id="3、Watcher"><a href="#3、Watcher" class="headerlink" title="3、Watcher"></a>3、Watcher</h6><p>Watcher是一个观察者对象。依赖收集以后Watcher对象会被保存在Dep的subs中，数据变动的时候Dep会通知Watcher实例，然后由Watcher实例回调cb进行视图的更新。</p>
<p><a target="_blank" rel="noopener" href="https://github.com/tangyuxian/Vue-learn/blob/master/core/observer/watcher.js">src&#x2F;core&#x2F;observer&#x2F;watcher.js</a></p>
<figure class="shiki javascript"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br></pre></div><div class="code"><pre class="shiki github-dark"><code><span class="line"><span style="color: #F97583">export</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">default</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Watcher</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">constructor</span><span style="color: #E1E4E8"> (</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">vm</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Component</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">expOrFn</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">string</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">|</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Function</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">cb</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Function</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">options</span><span style="color: #F97583">?:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Object</span></span>
<span class="line"><span style="color: #E1E4E8">  ) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.vm </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> vm</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*_watchers存放订阅者实例*/</span></span>
<span class="line"><span style="color: #E1E4E8">    vm._watchers.</span><span style="color: #B392F0">push</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">// options</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (options) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.deep </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">!!</span><span style="color: #E1E4E8">options.deep</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.user </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">!!</span><span style="color: #E1E4E8">options.user</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.lazy </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">!!</span><span style="color: #E1E4E8">options.lazy</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.sync </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">!!</span><span style="color: #E1E4E8">options.sync</span></span>
<span class="line"><span style="color: #E1E4E8">    } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.deep </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.user </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.lazy </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.sync </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">false</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.cb </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> cb</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">++</span><span style="color: #E1E4E8">uid </span><span style="color: #6A737D">// uid for batching</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.active </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">true</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.dirty </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.lazy </span><span style="color: #6A737D">// for lazy watchers</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.deps </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> []</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.newDeps </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> []</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.depIds </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">new</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Set</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.newDepIds </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">new</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Set</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.expression </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> process.env.</span><span style="color: #79B8FF">NODE_ENV</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">!==</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&#39;production&#39;</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">?</span><span style="color: #E1E4E8"> expOrFn.</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&#39;&#39;</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">// parse expression for getter</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*把表达式expOrFn解析成getter*/</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #F97583">typeof</span><span style="color: #E1E4E8"> expOrFn </span><span style="color: #F97583">===</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&#39;function&#39;</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.getter </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> expOrFn</span></span>
<span class="line"><span style="color: #E1E4E8">    } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.getter </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">parsePath</span><span style="color: #E1E4E8">(expOrFn)</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #F97583">!</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.getter) {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.</span><span style="color: #B392F0">getter</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">function</span><span style="color: #E1E4E8"> () {}</span></span>
<span class="line"><span style="color: #E1E4E8">        process.env.</span><span style="color: #79B8FF">NODE_ENV</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">!==</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&#39;production&#39;</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">&amp;&amp;</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">warn</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #9ECBFF">`Failed watching path: &quot;${</span><span style="color: #E1E4E8">expOrFn</span><span style="color: #9ECBFF">}&quot; `</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">+</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #9ECBFF">&#39;Watcher only accepts simple dot-delimited paths. &#39;</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">+</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #9ECBFF">&#39;For full control, use a function instead.&#39;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">          vm</span></span>
<span class="line"><span style="color: #E1E4E8">        )</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.value </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.lazy</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">?</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">undefined</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.</span><span style="color: #B392F0">get</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/**</span></span>
<span class="line"><span style="color: #6A737D">   * Evaluate the getter, and re-collect dependencies.</span></span>
<span class="line"><span style="color: #6A737D">   */</span></span>
<span class="line"><span style="color: #E1E4E8">   </span><span style="color: #6A737D">/*获得getter的值并且重新进行依赖收集*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">get</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*将自身watcher观察者实例设置给Dep.target，用以依赖收集。*/</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">pushTarget</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">let</span><span style="color: #E1E4E8"> value</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">const</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">vm</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.vm</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*执行了getter操作，看似执行了渲染操作，其实是执行了依赖收集。</span></span>
<span class="line"><span style="color: #6A737D">      在将Dep.target设置为自生观察者实例以后，执行getter操作。</span></span>
<span class="line"><span style="color: #6A737D">      譬如说现在的的data中可能有a、b、c三个数据，getter渲染需要依赖a跟c，</span></span>
<span class="line"><span style="color: #6A737D">      那么在执行getter的时候就会触发a跟c两个数据的getter函数，</span></span>
<span class="line"><span style="color: #6A737D">      在getter函数中即可判断Dep.target是否存在然后完成依赖收集，</span></span>
<span class="line"><span style="color: #6A737D">      将该观察者对象放入闭包中的Dep的subs中去。*/</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.user) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">try</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">        value </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.getter.</span><span style="color: #B392F0">call</span><span style="color: #E1E4E8">(vm, vm)</span></span>
<span class="line"><span style="color: #E1E4E8">      } </span><span style="color: #F97583">catch</span><span style="color: #E1E4E8"> (e) {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">handleError</span><span style="color: #E1E4E8">(e, vm, </span><span style="color: #9ECBFF">`getter for watcher &quot;${</span><span style="color: #79B8FF">this</span><span style="color: #9ECBFF">.</span><span style="color: #E1E4E8">expression</span><span style="color: #9ECBFF">}&quot;`</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">      value </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.getter.</span><span style="color: #B392F0">call</span><span style="color: #E1E4E8">(vm, vm)</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">// &quot;touch&quot; every property so they are all tracked as</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">// dependencies for deep watching</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*如果存在deep，则触发每个深层对象的依赖，追踪其变化*/</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.deep) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/*递归每一个对象或者数组，触发它们的getter，使得对象或数组的每一个成员都被依赖收集，形成一个“深（deep）”依赖关系*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #B392F0">traverse</span><span style="color: #E1E4E8">(value)</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*将观察者实例从target栈中取出并设置给Dep.target*/</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">popTarget</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.</span><span style="color: #B392F0">cleanupDeps</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">return</span><span style="color: #E1E4E8"> value</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/**</span></span>
<span class="line"><span style="color: #6A737D">   * Add a dependency to this directive.</span></span>
<span class="line"><span style="color: #6A737D">   */</span></span>
<span class="line"><span style="color: #E1E4E8">   </span><span style="color: #6A737D">/*添加一个依赖关系到Deps集合中*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">addDep</span><span style="color: #E1E4E8"> (</span><span style="color: #FFAB70">dep</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Dep</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">const</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">id</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> dep.id</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #F97583">!</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.newDepIds.</span><span style="color: #B392F0">has</span><span style="color: #E1E4E8">(id)) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.newDepIds.</span><span style="color: #B392F0">add</span><span style="color: #E1E4E8">(id)</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.newDeps.</span><span style="color: #B392F0">push</span><span style="color: #E1E4E8">(dep)</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #F97583">!</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.depIds.</span><span style="color: #B392F0">has</span><span style="color: #E1E4E8">(id)) {</span></span>
<span class="line"><span style="color: #E1E4E8">        dep.</span><span style="color: #B392F0">addSub</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/**</span></span>
<span class="line"><span style="color: #6A737D">   * Clean up for dependency collection.</span></span>
<span class="line"><span style="color: #6A737D">   */</span></span>
<span class="line"><span style="color: #E1E4E8">   </span><span style="color: #6A737D">/*清理依赖收集*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">cleanupDeps</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/*移除所有观察者对象*/</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">...</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/**</span></span>
<span class="line"><span style="color: #6A737D">   * Subscriber interface.</span></span>
<span class="line"><span style="color: #6A737D">   * Will be called when a dependency changes.</span></span>
<span class="line"><span style="color: #6A737D">   */</span></span>
<span class="line"><span style="color: #E1E4E8">   </span><span style="color: #6A737D">/*</span></span>
<span class="line"><span style="color: #6A737D">      调度者接口，当依赖发生改变的时候进行回调。</span></span>
<span class="line"><span style="color: #6A737D">   */</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">update</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">/* istanbul ignore else */</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.lazy) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.dirty </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">true</span></span>
<span class="line"><span style="color: #E1E4E8">    } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.sync) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/*同步则执行run直接渲染视图*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.</span><span style="color: #B392F0">run</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/*异步推送到观察者队列中，下一个tick时调用。*/</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #B392F0">queueWatcher</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/**</span></span>
<span class="line"><span style="color: #6A737D">   * Scheduler job interface.</span></span>
<span class="line"><span style="color: #6A737D">   * Will be called by the scheduler.</span></span>
<span class="line"><span style="color: #6A737D">   */</span></span>
<span class="line"><span style="color: #E1E4E8">   </span><span style="color: #6A737D">/*</span></span>
<span class="line"><span style="color: #6A737D">      调度者工作接口，将被调度者回调。</span></span>
<span class="line"><span style="color: #6A737D">    */</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">run</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.active) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #6A737D">/* get操作在获取value本身也会执行getter从而调用update更新视图 */</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">const</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">value</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.</span><span style="color: #B392F0">get</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span></span>
<span class="line"><span style="color: #E1E4E8">        value </span><span style="color: #F97583">!==</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.value </span><span style="color: #F97583">||</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #6A737D">// Deep watchers and watchers on Object/Arrays should fire even</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #6A737D">// when the value is the same, because the value may</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #6A737D">// have mutated.</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #6A737D">/*</span></span>
<span class="line"><span style="color: #6A737D">            即便值相同，拥有Deep属性的观察者以及在对象／数组上的观察者应该被触发更新，因为它们的值可能发生改变。</span></span>
<span class="line"><span style="color: #6A737D">        */</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">isObject</span><span style="color: #E1E4E8">(value) </span><span style="color: #F97583">||</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.deep</span></span>
<span class="line"><span style="color: #E1E4E8">      ) {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #6A737D">// set new value</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #F97583">const</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">oldValue</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.value</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #6A737D">/*设置新的值*/</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.value </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> value</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #6A737D">/*触发回调*/</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.user) {</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #F97583">try</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">            </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.cb.</span><span style="color: #B392F0">call</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.vm, value, oldValue)</span></span>
<span class="line"><span style="color: #E1E4E8">          } </span><span style="color: #F97583">catch</span><span style="color: #E1E4E8"> (e) {</span></span>
<span class="line"><span style="color: #E1E4E8">            </span><span style="color: #B392F0">handleError</span><span style="color: #E1E4E8">(e, </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.vm, </span><span style="color: #9ECBFF">`callback for watcher &quot;${</span><span style="color: #79B8FF">this</span><span style="color: #9ECBFF">.</span><span style="color: #E1E4E8">expression</span><span style="color: #9ECBFF">}&quot;`</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">          }</span></span>
<span class="line"><span style="color: #E1E4E8">        } </span><span style="color: #F97583">else</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.cb.</span><span style="color: #B392F0">call</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.vm, value, oldValue)</span></span>
<span class="line"><span style="color: #E1E4E8">        }</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/**</span></span>
<span class="line"><span style="color: #6A737D">   * Evaluate the value of the watcher.</span></span>
<span class="line"><span style="color: #6A737D">   * This only gets called for lazy watchers.</span></span>
<span class="line"><span style="color: #6A737D">   */</span></span>
<span class="line"><span style="color: #E1E4E8">   </span><span style="color: #6A737D">/*获取观察者的值*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">evaluate</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.value </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.</span><span style="color: #B392F0">get</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.dirty </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">false</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/**</span></span>
<span class="line"><span style="color: #6A737D">   * Depend on all deps collected by this watcher.</span></span>
<span class="line"><span style="color: #6A737D">   */</span></span>
<span class="line"><span style="color: #E1E4E8">   </span><span style="color: #6A737D">/*收集该watcher的所有deps依赖*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">depend</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">let</span><span style="color: #E1E4E8"> i </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.deps.</span><span style="color: #79B8FF">length</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">while</span><span style="color: #E1E4E8"> (i</span><span style="color: #F97583">--</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.deps[i].</span><span style="color: #B392F0">depend</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/**</span></span>
<span class="line"><span style="color: #6A737D">   * Remove self from all dependencies&#39; subscriber list.</span></span>
<span class="line"><span style="color: #6A737D">   */</span></span>
<span class="line"><span style="color: #E1E4E8">   </span><span style="color: #6A737D">/*将自身从所有依赖收集订阅列表删除*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">teardown</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">   </span><span style="color: #F97583">...</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div></div></figure>



<h6 id="4、Dep"><a href="#4、Dep" class="headerlink" title="4、Dep"></a>4、Dep</h6><p>被Observer的data在触发 <strong>getter</strong> 时，<strong>Dep</strong> 就会收集依赖的 <strong>Watcher</strong> ，其实 <strong>Dep</strong> 就像刚才说的是一个书店，可以接受多个订阅者的订阅，当有新书时即在data变动时，就会通过 <strong>Dep</strong> 给 <strong>Watcher</strong> 发通知进行更新。</p>
<p><a target="_blank" rel="noopener" href="https://github.com/tangyuxian/Vue-learn/blob/master/core/observer/dep.js">src&#x2F;core&#x2F;observer&#x2F;dep.js</a></p>
<figure class="shiki javascript"><div class='codeblock'><div class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></div><div class="code"><pre class="shiki github-dark"><code><span class="line"><span style="color: #F97583">export</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">default</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Dep</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">static</span><span style="color: #E1E4E8"> </span><span style="color: #FFAB70">target</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">?</span><span style="color: #B392F0">Watcher</span><span style="color: #E1E4E8">;</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">id</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">number</span><span style="color: #E1E4E8">;</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">subs</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Array</span><span style="color: #E1E4E8">&lt;</span><span style="color: #B392F0">Watcher</span><span style="color: #E1E4E8">&gt;;</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #F97583">constructor</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> uid</span><span style="color: #F97583">++</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.subs </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> []</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*添加一个观察者对象*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">addSub</span><span style="color: #E1E4E8"> (</span><span style="color: #FFAB70">sub</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Watcher</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.subs.</span><span style="color: #B392F0">push</span><span style="color: #E1E4E8">(sub)</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*移除一个观察者对象*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">removeSub</span><span style="color: #E1E4E8"> (</span><span style="color: #FFAB70">sub</span><span style="color: #F97583">:</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Watcher</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">remove</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.subs, sub)</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*依赖收集，当存在Dep.target的时候添加观察者对象*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">depend</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">if</span><span style="color: #E1E4E8"> (Dep.target) {</span></span>
<span class="line"><span style="color: #E1E4E8">      Dep.target.</span><span style="color: #B392F0">addDep</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #6A737D">/*通知所有订阅者*/</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">notify</span><span style="color: #E1E4E8"> () {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #6A737D">// stabilize the subscriber list first</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">const</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">subs</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">this</span><span style="color: #E1E4E8">.subs.</span><span style="color: #B392F0">slice</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">for</span><span style="color: #E1E4E8"> (</span><span style="color: #F97583">let</span><span style="color: #E1E4E8"> i </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #79B8FF">0</span><span style="color: #E1E4E8">, l </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> subs.</span><span style="color: #79B8FF">length</span><span style="color: #E1E4E8">; i </span><span style="color: #F97583">&lt;</span><span style="color: #E1E4E8"> l; i</span><span style="color: #F97583">++</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">      subs[i].</span><span style="color: #B392F0">update</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div></div></figure>

<h5 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h5><p>其实在 <strong>Vue</strong> 中初始化渲染时，视图上绑定的数据就会实例化一个 <strong>Watcher</strong>，依赖收集就是是通过属性的 <strong>getter</strong> 函数完成的，文章一开始讲到的 <strong>Observer</strong> 、<strong>Watcher</strong> 、<strong>Dep</strong> 都与依赖收集相关。其中 <strong>Observer</strong> 与 <strong>Dep</strong>        是一对一的关系， <strong>Dep</strong> 与 <strong>Watcher</strong> 是多对多的关系，<strong>Dep</strong> 则是 <strong>Observer</strong> 和 <strong>Watcher</strong> 之间的纽带。依赖收集完成后，当属性变化会执行被 <strong>Observer</strong> 对象的 <strong>dep.notify()</strong> 方法，这个方法会遍历订阅者（Watcher）列表向其发送消息，        <strong>Watcher</strong> 会执行 <strong>run</strong> 方法去更新视图，我们再来看一张图总结一下：<br><img onerror="imgOnError(this);" data-fancybox="gallery" src="https://cdn.tangyuxian.com/PicGoImages/202202271819467.webp" alt="img" data-caption="img" loading="lazy"></p>
<ol>
<li><p>在 <strong>Vue</strong> 中模板编译过程中的指令或者数据绑定都会实例化一个 <strong>Watcher</strong> 实例，实例化过程中会触发 <strong>get()</strong> 将自身指向 <strong>Dep.target</strong>;</p>
</li>
<li><p>data在 <strong>Observer</strong> 时执行 <strong>getter</strong> 会触发 <strong>dep.depend()</strong> 进行依赖收集;依赖收集的结果：</p>
<p>  1.data在 <strong>Observer</strong> 时闭包的dep实例的subs添加观察它的 <strong>Watcher</strong> 实例；</p>
<p>  2.Watcher** 的deps中添加观察对象 <strong>Observer</strong> 时的闭包dep；</p>
</li>
<li><p>当data中被 <strong>Observer</strong> 的某个对象值变化后，触发subs中观察它的watcher执行 <strong>update()</strong> 方法，最后实际上是调用watcher的回调函数cb，进而更新视图。</p>
</li>
</ol>
<hr>
<p>参考文档:<a target="_blank" rel="noopener" href="https://juejin.cn/post/6844903561327820808">掘金文章:理解vue相应原理</a></p>

    
  </article>

  
      
    <div class="nexmoe-post-copyright">
        <strong>本文作者：</strong>tangyuxian<br>
        
        <strong>本文链接：</strong><a href="https://www.tangyuxian.com/2021/02/23/%E5%89%8D%E7%AB%AF/vue/vue-%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86/" title="https:&#x2F;&#x2F;www.tangyuxian.com&#x2F;2021&#x2F;02&#x2F;23&#x2F;%E5%89%8D%E7%AB%AF&#x2F;vue&#x2F;vue-%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86&#x2F;" target="_blank" rel="noopener">https:&#x2F;&#x2F;www.tangyuxian.com&#x2F;2021&#x2F;02&#x2F;23&#x2F;%E5%89%8D%E7%AB%AF&#x2F;vue&#x2F;vue-%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86&#x2F;</a><br>

        
            <strong>版权声明：</strong>本文采用 <a href="https://creativecommons.org/licenses/by-nc-sa/3.0/cn/deed.zh" target="_blank">CC BY-NC-SA 3.0 CN</a> 协议进行许可

        
    </div>


  
  
  <div class="nexmoe-post-meta nexmoe-rainbow">
   
    
        <a class="nexmoefont icon-tag-fill -none-link" href="/tags/vue/" rel="tag">vue</a>
    
</div>
  
  
    <script async src="/js/copy-codeblock.js?v=1763992123966"></script>
  

  
      <div class="nexmoe-post-footer">
          <div class="valine"></div>
<script src='https://lib.baomitu.com/valine/1.3.9/Valine.min.js'></script>
<script>
  new Valine({
      el: '.valine',
      appId: 'r5zxC0st0DDjPA9auXzMV7HY-gzGzoHsz',
      appKey: '3bqCsovpyfTPHUzTHovd3V3V'
  })
</script>

      </div>
  
</div></div><div class="nexmoe-post-right">    <div class="nexmoe-fixed">
        <div class="nexmoe-tool">

            

            
            
            <button class="mdui-fab catalog" style="overflow:unset;">
                <i class="nexmoefont icon-i-catalog"></i>
                <div class="nexmoe-toc">
                    <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%80%BB%E8%A7%88"><span class="toc-number">1.</span> <span class="toc-text">总览</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A0%B9%E6%8D%AE%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E6%9D%A5%E5%81%9A%E4%B8%8D%E5%90%8C%E5%A4%84%E7%90%86%EF%BC%8C%E6%95%B0%E7%BB%84%E5%92%8C%E5%AF%B9%E8%B1%A1%E7%B1%BB%E5%9E%8B%E5%BD%93%E5%80%BC%E5%8F%98%E5%8C%96%E6%97%B6%E5%A6%82%E4%BD%95%E5%8A%AB%E6%8C%81%E3%80%82"><span class="toc-number">1.0.1.</span> <span class="toc-text">根据数据类型来做不同处理，数组和对象类型当值变化时如何劫持。</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E5%BC%8F%E6%B5%81%E7%A8%8B%EF%BC%9A"><span class="toc-number">1.0.2.</span> <span class="toc-text">响应式流程：</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E7%9B%B8%E5%85%B3%E7%9A%84%E5%86%85%E5%AE%B9%EF%BC%9A"><span class="toc-number">1.0.3.</span> <span class="toc-text">性能优化相关的内容：</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90"><span class="toc-number">2.</span> <span class="toc-text">源码分析</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%BA%90%E7%A0%81%E5%9C%B0%E5%9D%80"><span class="toc-number">2.0.1.</span> <span class="toc-text">源码地址</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%86%E6%9E%90"><span class="toc-number">2.0.2.</span> <span class="toc-text">分析</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#%E5%88%9D%E6%AD%A5"><span class="toc-number">2.0.2.0.1.</span> <span class="toc-text">初步</span></a></li></ol></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Vue%E5%88%9D%E5%A7%8B%E5%8C%96%E5%AE%9E%E4%BE%8B"><span class="toc-number">2.0.2.1.</span> <span class="toc-text">Vue初始化实例</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#1%E3%80%81initData"><span class="toc-number">2.0.2.1.1.</span> <span class="toc-text">1、initData</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#2%E3%80%81Observer"><span class="toc-number">2.0.2.1.2.</span> <span class="toc-text">2、Observer</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#3%E3%80%81Watcher"><span class="toc-number">2.0.2.1.3.</span> <span class="toc-text">3、Watcher</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#4%E3%80%81Dep"><span class="toc-number">2.0.2.1.4.</span> <span class="toc-text">4、Dep</span></a></li></ol></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-number">2.0.2.2.</span> <span class="toc-text">总结</span></a></li></ol></li></ol></li></ol></li></ol>
                </div>
            </button>
            

            

            <a href="#nexmoe-content" class="backtop toc-link" aria-label="Back To Top" title="top"><button class="mdui-fab mdui-ripple"><i class="nexmoefont icon-caret-top"></i></button></a>
        </div>
    </div>
</div></div><div id="nexmoe-pendant"><div class="nexmoe-drawer mdui-drawer nexmoe-pd" id="drawer">
    
        
            <div class="nexmoe-pd-item">
                <div class="clock">
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="memory"></div>
        <div class="needle" id="hours"></div>
        <div class="needle" id="minutes"></div>
        <div class="needle" id="seconds"></div>
        <div class="clock_logo">

        </div>

    </div>
<script>
        // ----- Variable declaration

        var data = {
                dt: new Date(),
                hours: {
                        el: document.querySelector('#hours'),
                        val: 0 },

                minutes: {
                        el: document.querySelector('#minutes'),
                        val: 0 },

                seconds: {
                        el: document.querySelector('#seconds'),
                        val: 0 },

                renderTime: 1000 };


        // ----- Clock rendering logic

        updateTime = () => {
                let hour = data.dt.getHours();
                let minute = data.dt.getMinutes();
                let second = data.dt.getSeconds();

                data.hours.el.classList.add('moving');
                data.minutes.el.classList.add('moving');
                data.seconds.el.classList.add('moving');

                data.hours.val = hour == 0 && data.hours.val == 59 * 6 * 5 ? 360 : hour * 6 * 5;
                data.minutes.val = minute == 0 && data.minutes.val == 59 * 6 ? 360 : minute * 6;
                data.seconds.val = second == 0 && data.seconds.val == 59 * 6 ? 360 : second * 6;

                data.hours.el.style.transform = `translate(-50%, -75%) rotate(${data.hours.val}deg)`;
                data.minutes.el.style.transform = `translate(-50%, -75%) rotate(${data.minutes.val}deg)`;
                data.seconds.el.style.transform = `translate(-50%, -75%) rotate(${data.seconds.val}deg)`;

                data.dt.setSeconds(second + 1);
        };

        // ----- Clock execution

        setInterval(() => {
                updateTime();
        }, data.renderTime);

        updateTime();

        // ----- Events area

        data.seconds.el.addEventListener('transitionend', () => {
                data.hours.el.classList.remove('moving');
                data.minutes.el.classList.remove('moving');
                data.seconds.el.classList.remove('moving');

                // if (data.hours.val == 360) {
                //   data.hours.el.style.transform = 'translate(-50%, -75%) rotate(0deg)';
                // }

                if (data.minutes.val == 360) {
                        data.minutes.el.style.transform = 'translate(-50%, -75%) rotate(0deg)';
                }

                if (data.seconds.val == 360) {
                        data.seconds.el.style.transform = 'translate(-50%, -75%) rotate(0deg)';
                }
        });

</script>
<style>
    .clock {
        background-color: #ffffff;
        width: 70vw;
        height: 70vw;
        max-width: 70vh;
        max-height: 70vh;
        border: solid 2.8vw var(--color-primary);
        position: relative;
        overflow: hidden;
        border-radius: 50%;
        box-sizing: border-box;
        box-shadow: 0 1.4vw 2.8vw var(--color2);
        zoom:0.2
    }

    .memory {
        position: absolute;
        top: 50%;
        left: 50%;
        transform-origin: center;
    }

    .memory:nth-child(1) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(0deg) translateY(-520%);
    }

    .memory:nth-child(2) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(6deg) translateY(-1461%);
    }

    .memory:nth-child(3) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(12deg) translateY(-1461%);
    }

    .memory:nth-child(4) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(18deg) translateY(-1461%);
    }

    .memory:nth-child(5) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(24deg) translateY(-1461%);
    }

    .memory:nth-child(6) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(30deg) translateY(-520%);
    }

    .memory:nth-child(7) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(36deg) translateY(-1461%);
    }

    .memory:nth-child(8) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(42deg) translateY(-1461%);
    }

    .memory:nth-child(9) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(48deg) translateY(-1461%);
    }

    .memory:nth-child(10) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(54deg) translateY(-1461%);
    }

    .memory:nth-child(11) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(60deg) translateY(-520%);
    }

    .memory:nth-child(12) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(66deg) translateY(-1461%);
    }

    .memory:nth-child(13) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(72deg) translateY(-1461%);
    }

    .memory:nth-child(14) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(78deg) translateY(-1461%);
    }

    .memory:nth-child(15) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(84deg) translateY(-1461%);
    }

    .memory:nth-child(16) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(90deg) translateY(-520%);
    }

    .memory:nth-child(17) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(96deg) translateY(-1461%);
    }

    .memory:nth-child(18) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(102deg) translateY(-1461%);
    }

    .memory:nth-child(19) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(108deg) translateY(-1461%);
    }

    .memory:nth-child(20) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(114deg) translateY(-1461%);
    }

    .memory:nth-child(21) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(120deg) translateY(-520%);
    }

    .memory:nth-child(22) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(126deg) translateY(-1461%);
    }

    .memory:nth-child(23) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(132deg) translateY(-1461%);
    }

    .memory:nth-child(24) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(138deg) translateY(-1461%);
    }

    .memory:nth-child(25) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(144deg) translateY(-1461%);
    }

    .memory:nth-child(26) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(150deg) translateY(-520%);
    }

    .memory:nth-child(27) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(156deg) translateY(-1461%);
    }

    .memory:nth-child(28) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(162deg) translateY(-1461%);
    }

    .memory:nth-child(29) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(168deg) translateY(-1461%);
    }

    .memory:nth-child(30) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(174deg) translateY(-1461%);
    }

    .memory:nth-child(31) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(180deg) translateY(-520%);
    }

    .memory:nth-child(32) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(186deg) translateY(-1461%);
    }

    .memory:nth-child(33) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(192deg) translateY(-1461%);
    }

    .memory:nth-child(34) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(198deg) translateY(-1461%);
    }

    .memory:nth-child(35) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(204deg) translateY(-1461%);
    }

    .memory:nth-child(36) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(210deg) translateY(-520%);
    }

    .memory:nth-child(37) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(216deg) translateY(-1461%);
    }

    .memory:nth-child(38) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(222deg) translateY(-1461%);
    }

    .memory:nth-child(39) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(228deg) translateY(-1461%);
    }

    .memory:nth-child(40) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(234deg) translateY(-1461%);
    }

    .memory:nth-child(41) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(240deg) translateY(-520%);
    }

    .memory:nth-child(42) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(246deg) translateY(-1461%);
    }

    .memory:nth-child(43) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(252deg) translateY(-1461%);
    }

    .memory:nth-child(44) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(258deg) translateY(-1461%);
    }

    .memory:nth-child(45) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(264deg) translateY(-1461%);
    }

    .memory:nth-child(46) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(270deg) translateY(-520%);
    }

    .memory:nth-child(47) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(276deg) translateY(-1461%);
    }

    .memory:nth-child(48) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(282deg) translateY(-1461%);
    }

    .memory:nth-child(49) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(288deg) translateY(-1461%);
    }

    .memory:nth-child(50) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(294deg) translateY(-1461%);
    }

    .memory:nth-child(51) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(300deg) translateY(-520%);
    }

    .memory:nth-child(52) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(306deg) translateY(-1461%);
    }

    .memory:nth-child(53) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(312deg) translateY(-1461%);
    }

    .memory:nth-child(54) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(318deg) translateY(-1461%);
    }

    .memory:nth-child(55) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(324deg) translateY(-1461%);
    }

    .memory:nth-child(56) {
        background-color: var(--color-primary);
        width: 2%;
        height: 8%;
        transform: translate(-50%, -50%) rotate(330deg) translateY(-520%);
    }

    .memory:nth-child(57) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(336deg) translateY(-1461%);
    }

    .memory:nth-child(58) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(342deg) translateY(-1461%);
    }

    .memory:nth-child(59) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(348deg) translateY(-1461%);
    }

    .memory:nth-child(60) {
        background-color: #949494;
        width: 1%;
        height: 3%;
        transform: translate(-50%, -50%) rotate(354deg) translateY(-1461%);
    }

    .needle {
        position: absolute;
        top: 50%;
        left: 50%;
        transform-origin: center;
    }

    .needle#hours {
        background-color: #1f1f1f;
        width: 4%;
        height: 30%;
        transform-origin: center 75%;
        transform: translate(-50%, -75%);
    }

    .needle#hours.moving {
        transition: transform 150ms ease-out;
    }

    .needle#hours:after {
        content: '';
        background-color: #1f1f1f;
        width: 4vw;
        height: 4vw;
        max-width: 4vh;
        max-height: 4vh;
        display: block;
        position: absolute;
        top: 75%;
        left: 50%;
        box-sizing: border-box;
        border-radius: 50%;
        transform: translate(-50%, -50%);
    }

    .needle#minutes {
        background-color: #1f1f1f;
        width: 2%;
        height: 45%;
        transform-origin: center 75%;
        transform: translate(-50%, -75%);
    }

    .needle#minutes.moving {
        transition: transform 150ms ease-out;
    }

    .needle#minutes:after {
        content: '';
        background-color: #1f1f1f;
        width: 4vw;
        height: 4vw;
        max-width: 4vh;
        max-height: 4vh;
        display: block;
        position: absolute;
        top: 75%;
        left: 50%;
        box-sizing: border-box;
        border-radius: 50%;
        transform: translate(-50%, -50%);
    }

    .needle#seconds {
        background-color: #cb2f2f;
        width: 1%;
        height: 50%;
        transform-origin: center 75%;
        transform: translate(-50%, -75%);
    }

    .needle#seconds.moving {
        transition: transform 150ms ease-out;
    }

    .needle#seconds:after {
        content: '';
        background-color: #cb2f2f;
        width: 2.5vw;
        height: 2.5vw;
        max-width: 2.5vh;
        max-height: 2.5vh;
        display: block;
        position: absolute;
        top: 75%;
        left: 50%;
        box-sizing: border-box;
        border-radius: 50%;
        transform: translate(-50%, -50%);
    }
    .clock_logo{
        width: 10vw;
        height: 10vw;
        max-width: 10vh;
        max-height: 10vh;
        position: absolute;
        top: 50%;
        left: 50%;
        box-sizing: border-box;
        border-radius: 50%;
        transform: translate(-50%, -50%);
        background-size: 100% 100%;
        background-repeat: no-repeat;
    }
    .clock_logo{
            width: 10vw;
            height: 10vw;
            max-width: 10vh;
            max-height: 10vh;
            position: absolute;
            top: 50%;
            left: 50%;
            box-sizing: border-box;
            border-radius: 50%;
            transform: translate(-50%, -50%);
            background-image: var(--clock-logo);
            background-size: 100% 100%;
            background-repeat: no-repeat;
    }
    @media (min-width: 100vh) {
        .clock {
            border: solid 2.8vh var(--color-primary);
            box-shadow: 0 1.4vh 2.8vh var(--color2);
        }
    }

</style>





            </div>
        
            <div class="nexmoe-pd-item">
                <div class="qweather" >
    <div id="he-plugin-standard">
        <iframe allowtransparency="true" frameborder="0" width="304" height="96" scrolling="no" src="//tianqi.2345.com/plugin/widget/index.htm?s=2&z=1&t=0&v=0&d=2&bd=0&k=000000&f=ff8040&ltf=009944&htf=cc0000&q=1&e=1&a=1&c=54511&w=300&h=96&align=center"></iframe>
    </div>
    <div class="qweather-logo">

    </div>
</div>
<style>
    .qweather{
        position: relative;
        padding-right: 5px;
    }
    .qweather #he-plugin-standard {
        border: 2px solid var(--color-primary);
        box-shadow: 0 1px 0 0 var(--color-primary), 0 1px 2px 0 var(--color-primary);
        border-radius: 8px;
        width: 300px;
        overflow: hidden;
    }
    .qweather-logo{
        position: absolute;
        right: 0;
        top: -15px;
        width: 40px;
        height: 40px;
        background-image: var(--qweather-logo);
        background-size: 100% 100%;
        background-repeat: no-repeat;
    }
</style>

            </div>
        
    
</div>
<style>
    .nexmoe-pd {
        left: auto;
        top: 40px;
        right: 0;
    }
    .nexmoe-pd-item{
       display: flex;
        justify-content: center;
        margin-bottom: 30px;
    }
    #nexmoe-pendant .mdui-drawer{
        width: 305px;
    }
    @media screen and (max-width: 1600px) {
        #nexmoe-pendant {
            display: none
        }
    }
</style>
</div><div id="nexmoe-footer"><!--!--></div><div id="nexmoe-search-space"><div class="search-container"><div class="search-header"><div class="search-input-container"><input class="search-input" type="text" placeholder="搜索" onInput="sinput();"></div><a class="search-close" onclick="sclose();">×</a></div><div class="search-body"></div></div></div><div></div><!-- hexo injector body_end start -->
<script src="https://cdn.jsdelivr.net/npm/hexo-shiki-plugin@latest/lib/codeblock.js"></script>

  <script>
  const CODE_CONFIG = {
    beautify: true,
    highlightCopy: true,
    highlightLang: true,
    highlightHeightLimit: null,
    isHighlightShrink: false,
    copy: {
      success: 'Copy Success',
      error: 'Copy Error',
      noSupport: 'Browser Not Support',
    }
  };
  console.log(
    `%c hexo-shiki-plugin %c v1.0.27 %c https://github.com/nova1751/hexo-shiki-plugin`,
    "color: #fff; background: #5f5f5f",
    "color: #fff; background: #80c8f8",
    ""
  );
  </script>
  <!-- hexo injector body_end end --><script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"model":{"jsonPath":"/live2dw/assets/xiaomai.model.json"},"display":{"position":"left","width":200,"height":500},"mobile":{"show":false},"react":{"opacity":0.9},"log":false,"pluginJsPath":"lib/","pluginModelPath":"assets/","pluginRootPath":"live2dw/","tagMode":false});</script></body></html>